package handler

import (
	"reflect"
	"strings"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/logger"
	"gorm.io/gorm"

	"ssforward/common/constant"
	"ssforward/common/enums"
	"ssforward/common/utils"
	"ssforward/internal/data/clients"
)

type DbForwardHandler struct {
	Orm *gorm.DB
}

func (e *DbForwardHandler) ForwardData(ruleConfig map[string]interface{}, jsonData map[string]interface{}) {
	serverObj, ok := ruleConfig["server"]
	if !ok || serverObj == nil {
		return
	}
	server := serverObj.(map[string]interface{})
	serverId := server["id"].(string)
	clientId := enums.Database.Get().Value + "-" + serverId
	dbTable := server["dbTable"].(string)
	dbDriver := server["dbDriver"].(string)
	variableMap := ruleConfig["variableMap"].(map[string]interface{})
	if dbPoint, ok := clients.ForwardDbClients.Load(clientId); ok {
		if len(dbTable) == 0 {
			dbTable = constant.AutoTableName
			// 自动创建表 - 根据字段映射创建表结构
			autoCreateTable(dbPoint.(*gorm.DB), dbTable, dbDriver, variableMap)
		}
		hasContent, err := forwardDbData(dbPoint.(*gorm.DB), dbTable, variableMap, jsonData)
		if !hasContent {
			return
		}
		if err == nil {
			_ = env.Cache.HashSet(constant.ForwardClientPrefix+serverId, map[string]interface{}{constant.ForwardClientLastTime: utils.GetNowTimeStr()})
			_, _ = env.Cache.HIncrBy(constant.ForwardClientPrefix+serverId, constant.ForwardClientSendCount, 1)
			logger.Debugf("db数据转发成功 dbname=[%+v] dbtable=[%+v] clientId=[%+v]", server["dbName"], dbTable, clientId)
			addToDetail(serverId)
		} else {
			logger.Errorf("db数据转发失败 dbname=[%+v] dbtable=[%+v] clientId=[%+v]", server["dbName"], dbTable, clientId)
		}
	} else {
		logger.Errorf("db转发数据连接失效 clientId=[%+v]", clientId)
	}
}

// 自动创建表
func autoCreateTable(db *gorm.DB, tableName, dbDriver string, variableMap map[string]interface{}) {
	if len(variableMap) == 0 {
		return
	}
	// 存在则不用创建(以第一次建表结构为准，后续字段映射改动将无法转存)
	if db.Migrator().HasTable(tableName) {
		return
	}
	// mysql初始化数据库时候用
	if dbDriver == enums.DriverOfMysql.Get().Name {
		db.Set("gorm:table_options", "ENGINE=InnoDB CHARACTER SET=utf8mb4")
	}
	// 构建表结构
	var structFields = make([]reflect.StructField, 0, len(variableMap))
	for _, to := range variableMap {
		field := to.(string)
		structFields = append(structFields, reflect.StructField{
			Name: utils.UpperFirstString(field),
			Type: reflect.TypeOf("string"),
			Tag:  reflect.StructTag(`json:"` + field + `" gorm:"type:varchar(255)"`),
		})
	}
	structType := reflect.StructOf(structFields)
	tableStruct := reflect.New(structType).Elem().Interface()
	// 建表
	logger.Infof("db数据转发创建缺省表 dbname=[%+v] dbtable=[%+v]", db.Migrator().CurrentDatabase(), tableName)
	if err := db.Table(tableName).Migrator().CreateTable(tableStruct); err != nil {
		logger.Error("自动创建数据表出现错误，err=[%+v]", err)
	}

}

// 转存数据
func forwardDbData(db *gorm.DB, tableName string, variableMap map[string]interface{}, jsonData map[string]interface{}) (bool, error) {
	insertMap := dataToMap(variableMap, jsonData)
	if len(insertMap) == 0 {
		return false, nil
	}
	if err := db.Table(tableName).Create(insertMap).Error; err != nil {
		return true, err
	}
	return true, nil
}

// 数据字段映射、拉平 or 不作处理
func dataToMap(variableMap map[string]interface{}, jsonData map[string]interface{}) map[string]interface{} {
	insertMap := map[string]interface{}{}
	if len(variableMap) == 0 {
		return insertMap
	}
	dataMap := jsonData["data"].(map[string]interface{})
	// 数据拉平
	flattenData := map[string]interface{}{}
	utils.FlattenJson(dataMap, flattenData, []string{})
	// 固定字段
	for k, v := range jsonData {
		if k == "data" {
			continue
		}
		flattenData[k] = v
	}

	// 映射字段值
	for k, to := range variableMap {
		if to == nil {
			continue
		}
		if strings.Contains(k, ";") {
			// 多对1字段
			obj := map[string]interface{}{}
			for _, kk := range strings.Split(k, ";") {
				if strings.Contains(kk, ":") {
					sk := strings.Split(kk, ":")
					if v, ok := flattenData[sk[0]]; ok && v != nil {
						obj[sk[1]] = v
					}
				} else {
					if v, ok := flattenData[kk]; ok {
						obj[kk] = v
					}
				}
			}
			insertMap[to.(string)] = obj
		} else if _, ok := flattenData[k]; !ok {
			// 自定义值
			insertMap[k] = to
		} else if v, ok := flattenData[k]; ok {
			// 映射转换
			insertMap[utils.InterfaceToStr(to)] = v
		}
	}
	return insertMap
}
